//
//  TTURLImageLoader.m
//  Tint
//
//  Created by Kevin Wu on 3/12/12.
//  Copyright (c) 2012 Telligenty. All rights reserved.
//

#import "TTURLImageLoader.h"

@interface TTURLImageLoader (Private)

- (TTURLImageLoaderDownloadItem *)downloadItemByTarget:(id)target;

- (void)addressDownloadSuccessful:(NSString *)address;
- (void)addressDownloadFailed:(NSString *)address;

@end



@implementation TTURLImageLoader



TTSynthesizeSingleton(TTURLImageLoader);


#pragma mark - Accessors

@synthesize defaultCacheTimeoutInterval = _defaultCacheTimeoutInterval;

- (void)setDefaultCacheTimeoutInterval:(NSTimeInterval)defaultCacheTimeoutInterval {
    _defaultCacheTimeoutInterval = defaultCacheTimeoutInterval;
    if (_defaultCacheTimeoutInterval <= 0.0) _defaultCacheTimeoutInterval = TTTimeIntervalWeek() * 10;
}


#pragma mark - Memory

- (id)init {
    self = [super init];
    if (self) {
        _defaultCacheTimeoutInterval = TTTimeIntervalWeek() * 10;
        _downloadItems = [[NSMutableArray alloc] init];
        _diskCache = [TTDiskCache cacheByType:@"URLImage"];
    }
    return self;
}

- (void)dealloc {
    _defaultCacheTimeoutInterval = 0.0;
    TTRelease(_downloadItems);
    _diskCache = nil;
    [super dealloc];
}



#pragma mark - Public

- (void)loadImageWithAddress:(NSString *)address {
    [self loadImageWithAddress:address 
          cacheTimeoutInterval:_defaultCacheTimeoutInterval 
                        target:nil 
                       success:NULL 
                       failure:NULL 
              placeholderImage:nil 
                  failureImage:nil];
}

- (void)loadImageWithAddress:(NSString *)address 
        cacheTimeoutInterval:(NSTimeInterval)cacheTimeoutInterval 
                      target:(id)target 
                     success:(SEL)success 
                     failure:(SEL)failure 
            placeholderImage:(UIImage *)placeholderImage 
                failureImage:(UIImage *)failureImage {
    
    // Check address
    if (!TTIsStringWithText(address)) return;
    
    
    // Find old download
    TTURLImageLoaderDownloadItem *oldDownloadItem = [self downloadItemByTarget:target];
    
    
    // Set up new download
    TTURLImageLoaderDownloadItem *downloadItem = [_downloadItems firstObjectForKeyPath:@"address" equalToValue:address];
    if (downloadItem == nil) {
        downloadItem = [TTURLImageLoaderDownloadItem make];
        [_downloadItems addObject:downloadItem];
        downloadItem.address = address;
    }
    downloadItem.cacheTimeoutInterval = (cacheTimeoutInterval<=0) ? _defaultCacheTimeoutInterval : cacheTimeoutInterval;
    
    
    TTURLImageLoaderRequestItem *requestItem = [downloadItem.requestItems firstObjectForKeyPath:@"target" identicalToValue:target];
    if (requestItem == nil) {
        requestItem = [TTURLImageLoaderRequestItem make];
        [downloadItem.requestItems addObject:requestItem];
        requestItem.target = target;
    }
    requestItem.success = success;
    requestItem.failure = failure;
    requestItem.placeholderImage = placeholderImage;
    requestItem.failureImage = failureImage;
    
    
    // Cancel if needed
    if (oldDownloadItem && (oldDownloadItem != downloadItem)) {
        TTURLImageLoaderRequestItem *ri = [oldDownloadItem.requestItems firstObjectForKeyPath:@"target" identicalToValue:target];
        [oldDownloadItem.requestItems removeObject:ri];
        if (!TTIsArrayWithItems(oldDownloadItem.requestItems)) {
            [oldDownloadItem.request removeDelegate:self];
            [oldDownloadItem.request cancel];
            [_downloadItems removeObject:oldDownloadItem];
        }
    }
    
    
    
    // Check cache
    NSString *cacheKey = [[self class] cacheKeyFromAddress:address];
    
    if ([_diskCache hasCacheForKey:cacheKey]) {
        // Image is in cache
        [self addressDownloadSuccessful:address];
    } else {
        // Image is not in cache
        
        // Set placeholder first
        if (target && success && [target respondsToSelector:success]) {
            [target performSelector:success withObject:placeholderImage];
        }
        
        if (downloadItem.request) {
            // downloading
        } else {
            // Set up request to download
            
            TTHTTPRequest *request = [[TTHTTPRequest alloc] initWithURLAddress:address];
            downloadItem.request = request;
            request.destinationPath = [_diskCache cachePathForKey:cacheKey];
            [request addDelegate:self];
            [request startAsynchronous];
            TTRelease(request);
        }
        
    }
}



- (void)cancelDownloadingAddress:(NSString *)address {
    TTURLImageLoaderDownloadItem *di = [_downloadItems firstObjectForKeyPath:@"address" equalToValue:address];
    if (di == nil) return;
    
    [di.request removeDelegate:self];
    [di.request cancel];
    
    for (TTURLImageLoaderRequestItem *ri in di.requestItems) {
        if (ri.target && ri.failure && [ri.target respondsToSelector:ri.failure]) {
            [ri.target performSelector:ri.failure withObject:ri.failureImage];
        }
    }
    
    [di.requestItems removeAllObjects];
    [_downloadItems removeObject:di];
}

- (void)cancelRequestingTarget:(id)target {
    
    TTURLImageLoaderDownloadItem *di = [self downloadItemByTarget:target];
    TTURLImageLoaderRequestItem *ri = [di.requestItems firstObjectForKeyPath:@"target" identicalToValue:target];
    if ((di == nil) || (ri == nil)) return;
    
    if (ri.target && ri.failure && [ri.target respondsToSelector:ri.failure]) {
        [ri.target performSelector:ri.failure withObject:ri.failureImage];
    }
    
    [di.requestItems removeObject:ri];
    
    if (!TTIsArrayWithItems(di.requestItems)) {
        [di.request removeDelegate:self];
        [di.request cancel];
        [_downloadItems removeObject:di];
    }
}


- (BOOL)isAddressDownloading:(NSString *)address {
    return ([_downloadItems firstObjectForKeyPath:@"address" equalToValue:address] != nil);
}

- (BOOL)isTargetRequesting:(id)target {
    return ([self downloadItemByTarget:target] != nil);
}



+ (NSString *)cacheKeyFromAddress:(NSString *)address {
    return [[address md5Hash] uppercaseString];
}

- (void)removeCachedFileOfAddress:(NSString *)address {
    NSString *cacheKey = [[self class] cacheKeyFromAddress:address];
    [_diskCache removeCacheForKey:cacheKey];
}




#pragma mark - TTOperationDelegate

- (void)operationDidStart:(TTOperation *)operation {
}

- (void)operationUpdated:(TTOperation *)operation {
}

- (void)operationDidFail:(TTOperation *)operation withError:(NSError *)error {
    
    if (TTIsInstance(operation, [TTHTTPRequest class])) {
        
        TTURLImageLoaderDownloadItem *di = [_downloadItems firstObjectForKeyPath:@"request" identicalToValue:operation];
        if (di) [self addressDownloadFailed:di.address];
        
    }
    
}

- (void)operationWillFinish:(TTOperation *)operation {
}

- (void)operationDidFinish:(TTOperation *)operation {
    
    if (TTIsInstance(operation, [TTHTTPRequest class])) {
        
        TTURLImageLoaderDownloadItem *di = [_downloadItems firstObjectForKeyPath:@"request" identicalToValue:operation];
        if (di) [self addressDownloadSuccessful:di.address];
        
    }
    
}

@end



@implementation TTURLImageLoader (Private)

- (TTURLImageLoaderDownloadItem *)downloadItemByTarget:(id)target {
    for (TTURLImageLoaderDownloadItem *di in _downloadItems) {
        TTURLImageLoaderRequestItem *ri = [di.requestItems firstObjectForKeyPath:@"target" identicalToValue:target];
        if (ri) return di;
    }
    return nil;
}

- (void)addressDownloadSuccessful:(NSString *)address {
    TTURLImageLoaderDownloadItem *di = [_downloadItems firstObjectForKeyPath:@"address" equalToValue:address];
    if (di == nil) return;
    
    NSString *cacheKey = [[self class] cacheKeyFromAddress:address];
    if (![_diskCache hasCacheForKey:cacheKey]) {
        NSString *cachePath = [_diskCache cachePathForKey:cacheKey];
        [_diskCache moveFileAtPath:cachePath asKey:cacheKey];
    }
    
    UIImage *image = [_diskCache imageForKey:cacheKey];
    if (image) {
        for (TTURLImageLoaderRequestItem *ri in di.requestItems) {
            if (ri.target && ri.success && [ri.target respondsToSelector:ri.success]) {
                [ri.target performSelector:ri.success withObject:image];
            }
        }
    } else {
        for (TTURLImageLoaderRequestItem *ri in di.requestItems) {
            if (ri.target && ri.failure && [ri.target respondsToSelector:ri.failure]) {
                [ri.target performSelector:ri.failure withObject:ri.failureImage];
            }
        }
    }
    
    [di.requestItems removeAllObjects];
    [_downloadItems removeObject:di];
}

- (void)addressDownloadFailed:(NSString *)address {
    TTURLImageLoaderDownloadItem *di = [_downloadItems firstObjectForKeyPath:@"address" equalToValue:address];
    if (di == nil) return;
    
    
    for (TTURLImageLoaderRequestItem *ri in di.requestItems) {
        if (ri.target && ri.failure && [ri.target respondsToSelector:ri.failure]) {
            [ri.target performSelector:ri.failure withObject:ri.failureImage];
        }
    }
    
    
    [di.requestItems removeAllObjects];
    [_downloadItems removeObject:di];
}

@end




@implementation TTURLImageLoaderDownloadItem
@synthesize address = _address;
@synthesize request = _request;
@synthesize requestItems = _requestItems;
@synthesize cacheTimeoutInterval = _cacheTimeoutInterval;
- (id)init {
    self = [super init];
    if (self) {
        _requestItems = [[NSMutableArray alloc] init];
    }
    return self;
}
- (void)dealloc {
    TTRelease(_address);
    TTRelease(_request);
    TTRelease(_requestItems);
    [super dealloc];
}
@end

@implementation TTURLImageLoaderRequestItem
@synthesize target = _target;
@synthesize success = _success;
@synthesize failure = _failure;
@synthesize placeholderImage = _placeholderImage;
@synthesize failureImage = _failureImage;
- (void)dealloc {
    _target = nil;
    _success = NULL;
    _failure = NULL;
    TTRelease(_placeholderImage);
    TTRelease(_failureImage);
    [super dealloc];
}
@end

